/*
    This file is part of Posixcpp library.

    Posixcpp library is free software: you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public License
    as published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    Posixcpp library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU LesserGeneral Public
    License along with Posixcpp library. If not, see
    <http://www.gnu.org/licenses/>.
*/

#ifndef RINGBUFFER_HPP
#define	RINGBUFFER_HPP

#include <stdexcept>

namespace posixcpp
{
namespace containers
{
/**
 * @class RingBuffer
 * @brief FIFIO ring buffer
 * @param T type of the elements
 */
template <typename T>
class RingBuffer final
{
public:
    /**
     * @param Capacity размер буфера
     */
    RingBuffer( size_t Capacity );
    ~RingBuffer();

    /**
     * Enque element
     */
    void enqueue( T New_Item );
    /**
     * Dequeue element
     */
    void dequeue( T & Element );

    size_t length() const { return m_length; }
    size_t maxLength() const { return m_maxLength; }
	
	T & first() { return *m_elements[m_first]; }
	const T & first() const { return *m_elements[m_first]; }
	
	T & last() { return *m_elements[m_last]; }
	const T & last() const { return *m_elements[m_last]; }
	
private:
    const size_t m_capacity;
    size_t m_first;
    size_t m_last;
    size_t m_length;
    size_t m_maxLength;
    T * m_elements;
};

template <typename T>
RingBuffer<T>::RingBuffer( size_t Capacity ) :
    m_capacity(Capacity),
    m_first(0),
	m_last(0),
	m_length(0),
	m_maxLength(0)
{
    m_elements = new T[Capacity];
}

template <typename T>
RingBuffer<T>::~RingBuffer()
{
    delete [] m_elements;
}

template <typename T>
void RingBuffer<T>::enqueue( T New_Item )
{
    if (this->m_length >= this->m_capacity)
        throw std::overflow_error("No capcacity for insertion");

    if (this->m_length == 0)
    {
        this->m_elements[0] = New_Item;
        this->m_first = 1;
        this->m_last = 1;
    }
    else if (this->m_first <= this->m_last)
    {
        if (this->m_last < this->m_capacity)
            this->m_elements[this->m_last++] = New_Item;
        else
        {
            this->m_elements[0] = New_Item;
            this->m_last = 1;
        }
    }
    else
        this->m_elements[this->m_last++] = New_Item;

    ++this->m_length;

    if (this->m_length > this->m_maxLength)
        this->m_maxLength = this->m_length;
}

template <typename T>
void RingBuffer<T>::dequeue( T & Element )
{
    if (this->m_length == 0)
        throw std::underflow_error("No elemnts for deletion");

    Element = this->m_elements[this->m_first-1];
    --this->m_length;

    if (this->m_length == 0)
    {
        this->m_first = 0;
        this->m_last = 0;
    }
    else if (this->m_first <= this->m_last)
        ++this->m_first;
    else
    {
        if (this->m_first < this->m_capacity)
            ++this->m_first;
        else
            this->m_first = 1;
    }
}
}
}

#endif	/* RINGBUFFER_HPP */

